/**
 * Licensed under the MIT License
 *
 * Copyright (c) 2010 Alexandre Croiseaux
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 */
package marcel.display.gui.texts
{
	import flash.filters.BlurFilter;
	import flash.text.AntiAliasType;
	import flash.text.Font;
	import flash.text.StyleSheet;
	import flash.text.TextField;
	import flash.text.TextFormat;
	import marcel.debug.*;
	import marcel.utils.ObjectUtils;
	import marcel.utils.StringUtils;
	
	
	/**
	 * StylesManager used to store loaded CSS files and applies styles to text fields
	 * @author Alexandre Croiseaux
	 */
	public class StylesManager
	{
		//--------------------------------------------------------------------------
		//
		//  Private vars
		//
		//--------------------------------------------------------------------------
		private static var _css:Object = {};
		
		
		//--------------------------------------------------------------------------
		//
		//  Public methods
		//
		//--------------------------------------------------------------------------
		/**
		 * Add a CSS to StyleManager
		 * @param	id			: unique ID
		 * @param	CSSContent		: CSS content
		 * @return	boolean			: return false is insert failed (CSS with ID id is Allready register)
		 */
		public static function pushCSS(id:String, CSSContent:String):Boolean
		{
			if (_css[id] == null)
			{
				_css[id] = new StyleSheet();
				_css[id].parseCSS(CSSContent);
				debugi("CSS with id '" + id + "' registered!");
				return true;
			}
			return false;
		}
		
		/**
		 * Get a TextFormat from CSS style
		 * @param	sStyleName	: style name
		 * @param	sCssID		: css id of the css file
		 * @return	a textFormat instance
		 */
		public static function getTextFormat(sStyleName:String , sCssID:String = null):TextFormat
		{
			return (new StyleSheet()).transform(getStyle(sStyleName, sCssID));
		}
		
		/**
		 * Apply style from CSS to a TextField
		 * @param	tf	The textfield instance to apply style to
		 * @param	sStyleName	the style class name to find in registered css files
		 * @param	sCssID	the specific css file id to search style class in
		 * @param	extraPropsObj	an optional key/value object to apply extra textfield/textformat properties. e.g. {align:"right"}
		 */
		public static function formatTextField(tf:TextField, sStyleName:String, sCssID:String = null, extraPropsObj:Object = null):void
		{
			if (sStyleName.length == 0) return;
			
			var i:int;
			var classes:Array = StringUtils.trimSplit(sStyleName);// commas or spaces delimiter
			
			/////////////////////////////////
			// handle extra props
			/////////////////////////////////
			if (extraPropsObj)
			{
				if (!_css.hasOwnProperty("__extra__")) _css["__extra__"] = new StyleSheet();
				_css["__extra__"].setStyle("__extra-style__", extraPropsObj);
				classes.push("__extra-style__"); // add new css class
			}
			
			/////////////////////////////////
			// textfield props
			/////////////////////////////////
			tf.antiAliasType = AntiAliasType.ADVANCED;
			
			var _o:Object = { };
			for (i = 0; i < classes.length; i++)
			{
				_o = getTextFieldPropsParam(classes[i], sCssID, _o);
			}
			
			// In some flash player versions (ie 10.1.53.64...), textfield width is not kept if set before the autoSize
			// So we sort the object keys to be sure that the width is set AFTER the autosize.
			var a:Array = [];
			for (var prop:String in _o) if (prop) a.push(prop);
			a.sort(Array.CASEINSENSITIVE);
			for (i = 0; i < a.length; i++)
			{
				try { tf[a[i]] = _o[a[i]]; } catch (e:Error) { };
			}
			
			/////////////////////////////////
			// textformat props
			/////////////////////////////////
			_o = { };
			for (i = 0; i < classes.length; i++)
			{
				_o = ObjectUtils.merge(_o, getStyle(classes[i], sCssID));
			}
			var _tfm:TextFormat = (new StyleSheet()).transform(_o);
			for (var cssprop:String in _o)
			{
				try { if (_tfm[cssprop] == undefined) _tfm[cssprop] = _o[cssprop]; } catch (e:Error) { };
			}
				
			tf.defaultTextFormat = _tfm;
			tf.setTextFormat(_tfm);
			if (_tfm.font) tf.embedFonts = isFontEmbededByFontName(_tfm.font);
			if (!tf.embedFonts) tf.filters = [new BlurFilter(0, 0, 0)];
			
			// fix width bug
			var o:Object = tf.width;
			o = tf.height;
		}
		
		//--------------------------------------------------------------------------
		//
		//  Private methods
		//
		//--------------------------------------------------------------------------
		private static function isFontEmbededByFontName(sFontName : String) : Boolean
		{
			var fonts:Array = Font.enumerateFonts();
			for each (var oFontItem : Font in fonts) {
				if ( oFontItem.fontName == sFontName ) return true;
			}
			return false;
		}
		
		private static function getStyle(sStyleName:String, sCssID:String):Object
		{
			var _s:StyleSheet;
			
			// check in specified css
			if (_css.hasOwnProperty(sCssID))
			{
				_s = _css[sCssID];
				if (_s.styleNames.indexOf(sStyleName) > -1) return _s.getStyle(sStyleName);
				if (_s.styleNames.indexOf("." + sStyleName) > -1) return _s.getStyle("." + sStyleName);
			}
			
			// check in other css
			for (var cssId:String in _css)
			{
				_s = _css[cssId];
				if (_s.styleNames.indexOf(sStyleName) > -1) return _s.getStyle(sStyleName);
				if (_s.styleNames.indexOf("." + sStyleName) > -1) return _s.getStyle("." + sStyleName);
			}
			
			// not found
			debuge("No css found containing stylename '" + sStyleName + "'");
			return {};
		}
		
		private static function getTextFieldPropsParam(sStyleClass:String, sCssID:String, initObj:Object):Object {
			
			var _oTFParams:Object = initObj == null ? new Object() : initObj;
			var _o:Object = getStyle(sStyleClass, sCssID);
			for (var prop:String in _o) {
				switch(prop.toLowerCase()) {
					case "opacity":
						_oTFParams.alpha = StringUtils.extractNumberFromString(_o[prop]);
						break;
					case "top":
						_oTFParams.y = StringUtils.extractNumberFromString(_o[prop]);
						break;
					case "left":
						_oTFParams.x = StringUtils.extractNumberFromString(_o[prop]);
						break;
					case "bordercolor" :
						_oTFParams.border = true;
						_oTFParams.borderColor = StringUtils.convertCssColorToHexa(_o[prop]);
						break;
					case "backgroundcolor" :
						_oTFParams.background = true;
						_oTFParams.backgroundColor = StringUtils.convertCssColorToHexa(_o[prop]);
						break;
					case "width":
					case "height":
						_oTFParams[prop] = StringUtils.extractNumberFromString(_o[prop]);
						break;
					case "multiline":
					case "selectable":
						_oTFParams[prop] = _o[prop] == "true";
						break;
					case "wordwrap":
						_oTFParams.wordWrap = _o[prop] == "true";
						break;
					case "displayaspassword":
						_oTFParams.displayAsPassword = _o[prop] == "true";
						break;
					case "antialiastype":
						_oTFParams.antiAliasType = _o[prop];
						break;
					case "autosize":
						_oTFParams.autoSize = _o[prop];
						break;
					default:
						_oTFParams[prop] = _o[prop];
						break;
				}
			}
			return _oTFParams;
		}
	}
}